use web_time::{SystemTime, UNIX_EPOCH};
use egui_dy::eframe::egui::{vec2, Color32, CursorIcon, Event, Label, Sense, Stroke, Ui};
use crate::{char_utils::{char_to_italic_str, char_to_num, is_letter, is_op}, math::{Equation, Monomial, PolyChild, Polynomial, PropContent, Sign, Signed, Value, Variable}};

use super::set_math_font_size;

#[derive(Debug, Clone)]
pub struct EditMath {
    /// The [branch index, branch index, ... , position in the leave]
    pub cursor: Vec<usize>,
    root: EditingBranch,
}
#[derive(Debug, Clone)]
pub enum EditingBranch {
    Root(Vec<EditingBranch>),
    Char(char),
}

#[derive(Debug, PartialEq)]
pub struct ParsingError {
    pub message: &'static str,
}

pub enum ParseResult {
    Prop(PropContent),
    Val(Value),
}

impl ParsingError {
    pub fn new(message: &'static str) -> Self {
        Self { message }
    }
}

enum EditingResponse {
    InsertAfterChar(char),
    InsertBeforeChar(char),
    CursorOutLeft,
    CursorOutRight,
    DeleteCurrent,
    DeleteBefore,
    None,
}

enum ClickSide {
    None,
    Left,
    Right
}

impl EditMath {
    pub fn new(cursor: Vec<usize>, root: EditingBranch) -> Self {
        Self { cursor, root }
    }
    pub fn new_root() -> Self {
        Self {
            cursor: vec![0],
            root: EditingBranch::Root(Vec::new()),
        }
    }
    pub fn parse(&self) -> Result<ParseResult, ParsingError> {
        match &self.root {
            EditingBranch::Root(root) => parse_root(&root),
            EditingBranch::Char(_) => Err(ParsingError::new("Char should not be parsed individually. This may be a bug"))
        }
    }
}

fn parse_root(root: &[EditingBranch]) -> Result<ParseResult, ParsingError> {
    let mut value_results = root.split(|branch| {
        if let EditingBranch::Char(c) = branch {
            *c == '='
        } else {
            false
        }
    }).map(|part| {
        let mut index = 0;
        parse_into_value(part, &mut index)
    });
    let first = match value_results.next().unwrap() {
        Err(err) => if err == EMPTY_VALUE_ERR && value_results.count() == 1 {
            return Ok(ParseResult::Prop(PropContent::None));
        } else {
            return Err(err);
        },
        Ok(value) => value
    };
    let second = match value_results.next() {
        None => return Ok(ParseResult::Val(first)),
        Some(value_result) => match value_result {
            Err(err) => return Err(err),
            Ok(value) => value,
        }
    };
    if value_results.next().is_some() {
        return Err(ParsingError::new("Continued equality is not supported yet"));
    }
    Ok(ParseResult::Prop(PropContent::Eq(Equation::new(first, second))))
}

const EMPTY_VALUE_ERR: ParsingError = ParsingError { message: "Emtry value" };
const CHAR_ERROR: ParsingError = ParsingError { message: "This type of char is not supported yet" };
const ROOT_INSIDE_VALUE_BUG: ParsingError = ParsingError { message: "Root branch inside a value, this may well be a bug." };
const NUM_AFTER_VAL_ERR: ParsingError = ParsingError { message: "Syntax error: number after a variable" };
const CONTINUED_OPS_ERR: &str = "Two operators are placed together";
fn parse_into_value(part: &[EditingBranch], index: &mut usize) -> Result<Value, ParsingError> {
    if *index == part.len() {
        return Err(EMPTY_VALUE_ERR);
    };
    let mut uninit = false;
    let mut result = match part[*index] {
        EditingBranch::Char(c) => if is_letter(c) {
            Value::Var(c.into())
        } else if c.is_ascii_digit() {
            Value::Num(char_to_num(c))
        } else if c == '+' {
            uninit = true;
            Value::Signed(Signed::new(Sign::Pos, Box::new(Value::Num(0))))
        } else if c == '-' {
            uninit = true;
            Value::Signed(Signed::new(Sign::Neg, Box::new(Value::Num(0))))
        } else if c == '(' {
            *index += 1;
            parse_into_value(part, index)?
        } else if c == ')' {
            return Err(EMPTY_VALUE_ERR);
        } else{
            return Err(CHAR_ERROR)
        },
        EditingBranch::Root(_) => return Err(ROOT_INSIDE_VALUE_BUG)
    };
    *index += 1;
    while *index < part.len() {
        let branch = &part[*index];
        if let EditingBranch::Char(c) = branch {
            if *c == ')' {
                break;
            }
        }
        result = match result {
            Value::Var(_) => parse_next_with_var(part, index, result, branch, &mut uninit)?,
            Value::Num(number) => parse_next_with_num(part, index, result, branch, number, &mut uninit)?,
            Value::Mono(ref monomial) => parse_next_with_mono(part, index, result.clone(), branch, monomial.clone(), &mut uninit)?,
            Value::Signed(ref signed) => parse_next_with_signed(part, index, result.clone(), branch, signed.clone(), &mut uninit)?,
            Value::Poly(polynomial) => parse_next_with_poly(part, index, branch, polynomial, &mut uninit)?
        };
        *index += 1;
    }
    if uninit {
        Err(ParsingError::new("Expression not finished yet"))
    } else {
        Ok(result)
    }
}
fn parse_next_with_var(part: &[EditingBranch], index: &mut usize, current: Value, next: &EditingBranch, uninit: &mut bool) -> Result<Value, ParsingError> {
    match next {
        EditingBranch::Char(c) => if is_letter(*c) {
            let another = Value::Var((*c).into());
            Ok(Value::Mono(Monomial::new(vec![current, another])))
        } else if c.is_ascii_digit() {
            Err(NUM_AFTER_VAL_ERR)
        } else if *c == '(' {
            *index += 1;
            let another = parse_into_value(part, index)?;
            Ok(Value::Mono(Monomial::new(vec![current, another])))
        } else if *c == '-' {
            let c1 = PolyChild::new(Sign::Pos, current);
            let c2 = PolyChild::new(Sign::Neg, Value::Num(0));
            let poly = Polynomial::new(vec![c1, c2]);
            *uninit = true;
            Ok(Value::Poly(poly))
        } else if *c == '+' {
            let c1 = PolyChild::new(Sign::Pos, current);
            let c2 = PolyChild::new(Sign::Pos, Value::Num(0));
            let poly = Polynomial::new(vec![c1, c2]);
            *uninit = true;
            Ok(Value::Poly(poly))
        } else {
            Err(CHAR_ERROR)
        },
        EditingBranch::Root(_) => Err(ROOT_INSIDE_VALUE_BUG)
    }
}
fn parse_next_with_num(part: &[EditingBranch], index: &mut usize, current: Value, next: &EditingBranch, number: usize, uninit: &mut bool) -> Result<Value, ParsingError> {
    match next {
        EditingBranch::Char(c) => if is_letter(*c) {
            let another = Value::Var((*c).into());
            Ok(Value::Mono(Monomial::new(vec![current, another])))
        } else if *c == '(' {
            *index += 1;
            let another = parse_into_value(part, index)?;
            Ok(Value::Mono(Monomial::new(vec![current, another])))
        } else if c.is_ascii_digit() {
            Ok(Value::Num(number * 10 + char_to_num(*c)))
        } else if *c == '-' {
            let c1 = PolyChild::new(Sign::Pos, current);
            let c2 = PolyChild::new(Sign::Neg, Value::Num(0));
            let poly = Polynomial::new(vec![c1, c2]);
            *uninit = true;
            Ok(Value::Poly(poly))
        } else if *c == '+' {
            let c1 = PolyChild::new(Sign::Pos, current);
            let c2 = PolyChild::new(Sign::Pos, Value::Num(0));
            let poly = Polynomial::new(vec![c1, c2]);
            *uninit = true;
            Ok(Value::Poly(poly))
        } else {
            Err(CHAR_ERROR)
        },
        EditingBranch::Root(_) => Err(ROOT_INSIDE_VALUE_BUG)
    }
}
fn parse_next_with_mono(part: &[EditingBranch], index: &mut usize, current: Value, next: &EditingBranch, mut monomial: Monomial, uninit: &mut bool) -> Result<Value, ParsingError> {
    match next {
        EditingBranch::Char(c) => if is_letter(*c) {
            let another = Value::Var((*c).into());
            monomial.children.push(another);
            Ok(Value::Mono(monomial))
        } else if *c == '(' {
            *index += 1;
            let another = parse_into_value(part, index)?;
            monomial.children.push(another);
            Ok(Value::Mono(monomial))
        } else if c.is_ascii_digit() {
            Err(NUM_AFTER_VAL_ERR)
        } else if *c == '-' {
            let c1 = PolyChild::new(Sign::Pos, current);
            let c2 = PolyChild::new(Sign::Neg, Value::Num(0));
            let poly = Polynomial::new(vec![c1, c2]);
            *uninit = true;
            Ok(Value::Poly(poly))
        } else if *c == '+' {
            let c1 = PolyChild::new(Sign::Pos, current);
            let c2 = PolyChild::new(Sign::Pos, Value::Num(0));
            let poly = Polynomial::new(vec![c1, c2]);
            *uninit = true;
            Ok(Value::Poly(poly))
        } else {
            Err(CHAR_ERROR)
        },
        EditingBranch::Root(_) => Err(ROOT_INSIDE_VALUE_BUG)
    }
}
fn parse_next_with_signed(part: &[EditingBranch], index: &mut usize, current: Value, next: &EditingBranch, mut signed: Signed, uninit: &mut bool) -> Result<Value, ParsingError> {
    match next {
        EditingBranch::Char(c) => if is_letter(*c) {
            let another = Value::Var((*c).into());
            if *uninit {
                signed.inner = Box::new(another);
                *uninit = false;
                Ok(Value::Signed(signed))
            } else {
                match &mut *signed.inner {
                    Value::Mono(monomial) => monomial.children.push(another),
                    _ => *signed.inner = Value::Mono(Monomial::new(vec![*signed.inner, another]))
                }
                Ok(Value::Signed(signed))
            }
        } else if *c == '(' {
            *index += 1;
            let another = parse_into_value(part, index)?;
            if *uninit {
                signed.inner = Box::new(another);
                *uninit = false;
                Ok(Value::Signed(signed))
            } else {
                match &mut *signed.inner {
                    Value::Mono(monomial) => monomial.children.push(another),
                    _ => *signed.inner = Value::Mono(Monomial::new(vec![*signed.inner, another]))
                }
                Ok(Value::Signed(signed))
            }
        } else if c.is_ascii_digit() {
            let next = char_to_num(*c);
            if *uninit {
                signed.inner = Box::new(Value::Num(next));
                *uninit = false;
                Ok(Value::Signed(signed))
            } else {
                match *signed.inner {
                    Value::Num(number) => *signed.inner = Value::Num(number * 10 + next),
                    _ => return Err(NUM_AFTER_VAL_ERR),
                }
                Ok(Value::Signed(signed))
            }
        } else if *c == '-' {
            if *uninit {
                Err(ParsingError::new(CONTINUED_OPS_ERR))
            } else {
                let c1 = if let Sign::Neg = signed.sign {
                    signed.into()
                } else {
                    PolyChild::new(Sign::Pos, current)
                };
                let c2 = PolyChild::new(Sign::Neg, Value::Num(0));
                let poly = Polynomial::new(vec![c1, c2]);
                *uninit = true;
                Ok(Value::Poly(poly))
            }
        } else if *c == '+' {
            if *uninit {
                Err(ParsingError::new(CONTINUED_OPS_ERR))
            } else {
                let c1 = if let Sign::Neg = signed.sign {
                    signed.into()
                } else {
                    PolyChild::new(Sign::Pos, current)
                };
                let c2 = PolyChild::new(Sign::Pos, Value::Num(0));
                let poly = Polynomial::new(vec![c1, c2]);
                *uninit = true;
                Ok(Value::Poly(poly))
            }
        } else {
            Err(CHAR_ERROR)
        },
        EditingBranch::Root(_) => Err(ROOT_INSIDE_VALUE_BUG)
    }
}
fn parse_next_with_poly(part: &[EditingBranch], index: &mut usize, next: &EditingBranch, mut poly: Polynomial, uninit: &mut bool) -> Result<Value, ParsingError> {
    match next {
        EditingBranch::Char(c) => if is_letter(*c) {
            let another = Value::Var(Variable::new(*c, None));
            let last_child = poly.last_mut().unwrap();
            if *uninit {
                last_child.inner = another;
                *uninit = false;
                Ok(Value::Poly(poly))
            } else {
                let inner = &mut last_child.inner;
                match inner {
                    Value::Mono(monomial) => monomial.push(another),
                    _ => *inner = Value::Mono(Monomial::new(vec![inner.clone(), another]))
                }
                Ok(Value::Poly(poly))
            }
        } else if *c == '(' {
            *index += 1;
            let another = parse_into_value(part, index)?;
            let last_child = poly.last_mut().unwrap();
            if *uninit {
                last_child.inner = another;
                *uninit = false;
                Ok(Value::Poly(poly))
            } else {
                let inner = &mut last_child.inner;
                match inner {
                    Value::Mono(monomial) => monomial.push(another),
                    _ => *inner = Value::Mono(Monomial::new(vec![inner.clone(), another]))
                }
                Ok(Value::Poly(poly))
            }
        } else if c.is_ascii_digit() {
            let next = char_to_num(*c);
            let last_child = poly.last_mut().unwrap();
            if *uninit {
                last_child.inner = Value::Num(next);
                *uninit = false;
                Ok(Value::Poly(poly))
            } else {
                match last_child.inner {
                    Value::Num(ref mut number) => *number = *number * 10 + next,
                    _ => return Err(NUM_AFTER_VAL_ERR)
                }
                Ok(Value::Poly(poly))
            }
        } else if *c == '+' {
            if *uninit {
                Err(ParsingError::new(CONTINUED_OPS_ERR))
            } else {
                poly.push(PolyChild::new(Sign::Pos, Value::Num(0)));
                *uninit = true;
                Ok(Value::Poly(poly))
            }
        } else if *c == '-' {
            if *uninit {
                Err(ParsingError::new(CONTINUED_OPS_ERR))
            } else {
                poly.push(PolyChild::new(Sign::Neg, Value::Num(0)));
                *uninit = true;
                Ok(Value::Poly(poly))
            }
        } else {
            Err(CHAR_ERROR)
        },
        EditingBranch::Root(_) => Err(ROOT_INSIDE_VALUE_BUG)
    }
}

pub fn update_editing_math(ui: &mut Ui, editing_math: &mut EditMath) {
    let pressed_keys = ui.input(|i|
        i.events.iter().filter_map(|e| match e {
            Event::Text(s) => Some(s.to_owned()),
            Event::Key { key, pressed , .. } => {
                if *pressed {
                    let name = key.name();
                    if name.len() > 1 { Some(name.to_owned()) }
                    else { None }
                } else {
                    None
                }
            }
            _ => None
        }).collect::<Vec<String>>()
    );
    ui.horizontal(|ui| {
        ui.scope(|ui| {
            let size = 17.0;
            ui.style_mut().spacing.item_spacing.x = 0.0;
            update_editing_branch_with_cursor(
                ui,
                &mut editing_math.root,
                &mut editing_math.cursor,
                size,
                0,
                &pressed_keys,
            );
        });
    });
}

fn update_editing_branch(
    ui: &mut Ui,
    branch: &mut EditingBranch,
    size: f32,
    cursor: &mut Vec<usize>,
    pos: Vec<usize>,
) {
    set_math_font_size(ui, size);
    match branch {
        EditingBranch::Char(c) => {
            match render_edit_char(ui, *c, size) {
                ClickSide::Left => {
                    *cursor = pos;
                    cursor.push(0);
                },
                ClickSide::Right => {
                    *cursor = pos;
                    cursor.push(1);
                }
                _ => {}
            }
        }
        EditingBranch::Root(_) => {}
    };
}

fn update_editing_branch_with_cursor(
    ui: &mut Ui,
    branch: &mut EditingBranch,
    cursor: &mut Vec<usize>,
    size: f32,
    depth: usize,
    pressed_keys: &[String],
) -> EditingResponse {
    set_math_font_size(ui, size);
    match branch {
        EditingBranch::Root(root) => {
            update_editing_root(ui, root, cursor, size, depth, pressed_keys);
            EditingResponse::None
        }
        EditingBranch::Char(c) => {
            update_editing_char_with_cursor(ui, *c, cursor, size, depth, pressed_keys)
        }
    }
}

fn update_editing_root(
    ui: &mut Ui,
    root: &mut Vec<EditingBranch>,
    cursor: &mut Vec<usize>,
    size: f32,
    depth: usize,
    pressed_keys: &[String],
) {
    let mut editing_response = EditingResponse::None;
    let cursor_pos = cursor[depth];
    for (index, branch) in root.iter_mut().enumerate() {
        if index == cursor_pos {
            editing_response = update_editing_branch_with_cursor(
                ui,
                branch,
                cursor,
                size,
                depth + 1,
                pressed_keys,
            );
        } else {
            update_editing_branch(ui, branch, size, cursor, vec![index]);
        }
    }
    match editing_response {
        EditingResponse::InsertAfterChar(c) => {
            root.insert(cursor_pos + 1, EditingBranch::Char(c));
            cursor[depth] += 1;
        }
        EditingResponse::InsertBeforeChar(c) => {
            root.insert(cursor_pos, EditingBranch::Char(c));
            cursor[depth] += 1;
        }
        EditingResponse::CursorOutLeft => {
            if cursor_pos > 0 {
                cursor[depth] -= 1;
            }
        }
        EditingResponse::CursorOutRight => {
            if cursor_pos < root.len() - 1 {
                cursor[depth] += 1;
            }
        }
        EditingResponse::DeleteCurrent => {
            root.remove(cursor_pos);
            if cursor_pos > 0 {
                cursor[depth] -= 1;
            } else if root.len() > 0 {
                cursor[depth + 1] = 0;
            }
        },
        EditingResponse::DeleteBefore => if cursor_pos > 0 {
            root.remove(cursor_pos - 1);
            cursor[depth] -= 1;
        }
        EditingResponse::None => {}
    }
    if root.len() == 0 {
        render_cursor(ui, size);
        let char_keys = pressed_keys
            .iter()
            .filter(|key| key.len() == 1)
            .map(|key| key.chars().next().unwrap());
        for key in char_keys {
            if key.is_ascii_graphic() {
                root.push(EditingBranch::Char(key));
                cursor.push(1);
            }
        }
    }
}

fn update_editing_char_with_cursor(
    ui: &mut Ui,
    c: char,
    cursor: &mut Vec<usize>,
    size: f32,
    depth: usize,
    pressed_keys: &[String],
) -> EditingResponse {
    let cursor_pos = cursor[depth];
    let click_side = if cursor_pos == 0 {
        render_cursor(ui, size);
        render_edit_char(ui, c, size)
    } else {
        let click_side = render_edit_char(ui, c, size);
        render_cursor(ui, size);
        click_side
    };
    match click_side {
        ClickSide::Left => cursor[depth] = 0,
        ClickSide::Right => cursor[depth] = 1,
        _ => {}
    }
    for key in pressed_keys {
        if key.len() > 1 {
            if key == "Left" {
                if cursor_pos == 1 {
                    cursor[depth] = 0;
                } else {
                    return EditingResponse::CursorOutLeft;
                }
            } else if key == "Right" {
                if cursor_pos == 0 {
                    cursor[depth] = 1;
                } else {
                    return EditingResponse::CursorOutRight;
                }
            } else if key == "Backspace" {
                if cursor_pos == 0 {
                    return EditingResponse::DeleteBefore;
                } else {
                    return EditingResponse::DeleteCurrent;
                }
            }
        } else {
            let key = key.chars().next().unwrap();
            if key.is_ascii_graphic() {
                if cursor_pos == 0 {
                    return EditingResponse::InsertBeforeChar(key);
                } else {
                    return EditingResponse::InsertAfterChar(key);
                }
            }
        }
    }
    EditingResponse::None
}

fn render_edit_char(ui: &mut Ui, c: char, size: f32) -> ClickSide {
    let sense = Sense::click_and_drag();
    let label = Label::new(char_to_italic_str(c)).sense(sense).selectable(false);
    let res = if is_op(c) {
        let res1 = ui.allocate_response(vec2(size * 0.2, size), sense);
        let res2 = ui.add(label);
        let res3 = ui.allocate_response(vec2(size * 0.2, size), sense);
        res1 | res2 | res3
    } else {
        ui.add(label)
    };
    let res = res.on_hover_and_drag_cursor(CursorIcon::Text);
    if res.clicked() {
        let rect = res.rect;
        let pos = res.interact_pointer_pos().unwrap();
        if rect.contains(pos) {
            let middle_x = (rect.max.x + rect.min.x) * 0.5;
            if pos.x < middle_x {
                ClickSide::Left
            } else {
                ClickSide::Right
            }
        } else {
            ClickSide::None
        }
    } else {
        ClickSide::None
    }
}

fn render_cursor(ui: &mut Ui, size: f32) {
    let (_, rect) = ui.allocate_space(vec2(1.0, size));
    let pos = rect.min;
    let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let second = duration.as_millis();
    let color = if second % 1000 < 500 {
        Color32::LIGHT_GRAY
    } else {
        Color32::DARK_GRAY
    };
    ui.painter()
        .vline(pos.x, pos.y..=pos.y + size, Stroke::new(1.0, color));
}
